﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using AForge.Vision.Motion;
using AForge.Imaging.Filters;
using AForge;
using AForge.Imaging;
namespace FFPAnalyze
{
    public class Analyze
    {
        public bool IsAnalyze { get; private set; }
        public int FrameSkipRate { get; set; }
        public int MaxFrame { get; set; }
        public int MaxAlarmCount { get; set; }

        private int tick = 0;
        private int indexFrame = 0;
        private int AlarmCount = 0;
        public int AlamTimes { get; private set; }
        Dictionary<DateTime, Bitmap> alarmFrames = new Dictionary<DateTime, Bitmap>();

        public delegate void AlarmCallback(Bitmap frames);
        public AlarmCallback Alarm;
        BlobCountingObjectsProcessing countingDetector = null;
        public Analyze()
        {
            this.IsAnalyze = false;
            this.FrameSkipRate = 5;
            this.MaxAlarmCount = 1;
            this.MaxFrame = 150;
            this.AlamTimes = 0;

            countingDetector = (BlobCountingObjectsProcessing)detector.MotionProcessingAlgorithm;

           countingDetector.HighlightMotionRegions = false;
        }

        public delegate short HorizonAngleCallBack();
        public delegate short VerticalAngleCallBack();
        public HorizonAngleCallBack horizonAngleCallBackHandle;
        public  VerticalAngleCallBack verticalAngleCallBackHandle;
        /// <summary>
        /// 提供yv12的视频流
        /// </summary>
        /// <param name="buffer">每一帧的数据</param>
        /// <param name="width">每一帧图片的宽</param>
        /// <param name="height">每一帧图片的高</param>
        public void InputVideoStreamYV12(IntPtr buffer, ref  int width, ref int height)
        {
            if (++tick % this.FrameSkipRate != 0) return;
            else tick = 0;

            if (this.IsAnalyze)
            {
                if (indexFrame < this.MaxFrame)
                {
                    Bitmap frameAlarm = this.AnalyzeFrame(YV12DecodeRGB(buffer, ref width, ref height));

                    if (frameAlarm != null && this.Alarm != null)
                    {
                        if (AlarmCount < this.MaxAlarmCount)
                        {
                            this.alarmFrames.Add(DateTime.Now, frameAlarm);

                        }
                        else if (AlarmCount == this.MaxAlarmCount)
                        {
                            SaveAlarmBitmap(null, true);
                            foreach (KeyValuePair<DateTime, Bitmap> timeframe in this.alarmFrames)
                           {
                                SaveAlarmBitmap(timeframe.Value, false);
                                Alarm(timeframe.Value);
                           }
                        }
                        else
                        {
                            SaveAlarmBitmap(frameAlarm, false);
                            Alarm(frameAlarm);
                        }

                        AlarmCount++;
                    }

                    indexFrame++;
                }
                else
                {
                    IsAnalyze = false;
                    indexFrame = 0;
                    AlarmCount = 0;
                    alarmFrames.Clear();
                    Alarm(null);
                }
            }

        }

        #region Decode

        /// <summary>
        /// 将YV12解码为Grey
        /// </summary>
        /// <param name="pIn">YV12图像指针</param>
        /// <param name="frame">Grey图像,不能为空, 且尺寸与YV12相同</param>
        Bitmap frame = null;
        public Bitmap YV12DecodeGrey(IntPtr pIn, ref int width, ref int height)
        {
            if (frame == null)
                frame = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            int W = width;
            int H = height;
            long size = W * H;
            int indexY = 0;
            unsafe
            {
                byte* yData = (byte*)pIn.ToPointer();
                lock (frame)
                {
                    BitmapData dataOutGrey = frame.LockBits(new Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                    byte* pOutGrey = (byte*)dataOutGrey.Scan0.ToPointer();

                    for (int h = 0; h < H; h++)
                    {
                        for (int w = 0; w < W; w++)
                        {
                            indexY = h * W + w;

                            for (int i = 0; i < 3; i++)
                            {
                                pOutGrey[indexY * 3 + i] = yData[indexY];
                             
                            }

                        }
                     }

                    frame.UnlockBits(dataOutGrey);
                 }

            }
            return frame;

        }


        /// <summary>
        /// 将YV12解码为RGB
        /// </summary>
        /// <param name="pIn">YV12图像指针</param>
        /// <param name="frame">RGB图像,不能为空, 且尺寸与YV12相同</param>
        public Bitmap YV12DecodeRGB(IntPtr pIn, ref int width, ref int height)
        {
            if (frame == null)
            frame = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            int W = width;
            int H = height;
            long size = W * H;
            int indexY = 0, indexUV = 0;
            unsafe
            {
                byte* yData = (byte*)pIn.ToPointer();
                byte* vData = &yData[size];
                byte* uData = &vData[size >> 2];
                lock (frame)
                {
                    BitmapData dataOutRGB = frame.LockBits(new Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                    byte* pOutRGB = (byte*)dataOutRGB.Scan0.ToPointer();
                    byte y = 0, u = 0, v = 0;
                    double temp = 0;
                    for (int h = 0; h < H; h++)
                    {
                        for (int w = 0; w < W; w++)
                        {
                            indexY = h * W + w;
                            indexUV = h / 2 * W / 2 + w / 2;

                            y = yData[indexY];
                            v = vData[indexUV];
                            u = uData[indexUV];

                            temp = 1.164383 * (y - 16) + 2.017230 * (u - 128) + 0;
                            pOutRGB[indexY * 3 + 0] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                            temp = (1.164383 * (y - 16) - 0.391762 * (u - 128) - 0.812969 * (v - 128));
                            pOutRGB[indexY * 3 + 1] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                            temp = 1.164383 * (y - 16) + 0 + 1.596016 * (v - 128);
                            pOutRGB[indexY * 3 + 2] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                        }
                    }

                    frame.UnlockBits(dataOutRGB);
                }
            }
            return frame;
        }

        #endregion
       
     
        MotionDetector detector = new MotionDetector(
       new SimpleBackgroundModelingDetector(true, true), //  new TwoFramesDifferenceDetector( ),
       new BlobCountingObjectsProcessing());         //MotionAreaHighlighting() );


        //报警框总面积率
        const float RectAreaRate = 0.2f;
        //报警框最大尺寸
        static readonly Size RectMaxSize = new Size(100, 100);

        //报警框最小尺寸
        static readonly Size RectMinSize = new Size(5, 5);
        //报警框最大纹理标记数
        const int RectMaxTextureCount = 10;

        //是否显示被纹理过滤的报警框内的纹理(纹理用黄点标记)
        const bool IsShowRectTexture = false;

        //是否开启纹理测试
        const bool IsEnableTextureTest = false;
        /// <summary>
        /// 轮廓数量
        /// </summary>
        /// <param name="Bmp"></param>
        /// <param name="Rect"></param>
        /// <param name="IsChanged"></param>
        /// <returns></returns>
        int Contour(Bitmap Bmp, Rectangle Rect, bool IsChanged)
        {
            int Count = 0;

            if (Rect.Width > 1 && Rect.Height > 1)
            {
                for (int y = 0; y < Rect.Height - 1; y++)
                {
                    for (int x = 0; x < Rect.Width - 1; x++)
                    {
                        Color C = Bmp.GetPixel(Rect.X + x, Rect.Y + y);
                        Color C1 = Bmp.GetPixel(Rect.X + x + 1, Rect.Y + y);
                        Color C2 = Bmp.GetPixel(Rect.X + x, Rect.Y + y + 1);
                        Color C3 = Bmp.GetPixel(Rect.X + x + 1, Rect.Y + y + 1);

                        //平滑对抗
                        if (System.Math.Abs((float)(C.GetBrightness() - C1.GetBrightness())) > 0.1f
                            || System.Math.Abs((float)(C.GetBrightness() - C2.GetBrightness())) > 0.1f
                            || System.Math.Abs((float)(C.GetBrightness() - C3.GetBrightness())) > 0.1f)
                        {
                            Count++;
                            if (IsChanged)
                            {
                                Bmp.SetPixel(Rect.X + x, Rect.Y + y, Color.Yellow);
                            }
                        }
                    }
                }
            }

            return Count;
        }

        private float motionAlarmLevel = 0.02f;
        /// <summary>
        /// 对每一帧进行烟火识别
        /// </summary>
        /// <param name="frame"></param>
        /// <returns></returns>
        /// 
        private Bitmap AnalyzeFrame(Bitmap frame)
        {
            int cnt = 0;
            Bitmap image =(Bitmap)frame.Clone();
            float motionLevel = detector.ProcessFrame(image);

            if (motionLevel < motionAlarmLevel) return null;
            Rectangle[] SmokeRects = countingDetector.ObjectRectangles;

            int Area = 0;

            foreach (Rectangle rect in SmokeRects)
            {
                Area += rect.Width * rect.Height;
            }

            if (Area > image.Width * image.Height * RectAreaRate)
            {
                return null;
            }
            Graphics g = Graphics.FromImage(image);

            foreach (Rectangle rect in SmokeRects)
            {
                //面积过滤
                if (rect.Width < RectMinSize.Width || rect.Height < RectMinSize.Height
                     || rect.Width > RectMaxSize.Width || rect.Height > RectMaxSize.Height)
                {
                    continue;
                }

                //纹理过滤
                if (Contour(image, rect, IsShowRectTexture) > RectMaxTextureCount)
                    continue;

                //纹理测试
                if (IsEnableTextureTest)
                    Contour(image, new Rectangle(0, 0, image.Width, image.Height), true);

                g.DrawRectangle(new Pen(Color.Red, 2), rect);
                cnt++;
            }
            if (cnt > 0)
                return image;
            else
                return null;
        }
        public delegate string GetImagePathCallback(bool isFirstFrame);
        public GetImagePathCallback GetImagePath;
        public void SaveAlarmBitmap(Bitmap bmp, bool isFirstFrame)
        {
            if (GetImagePath != null)
            {
                if (bmp != null)
                {
                    bmp.Save(GetImagePath(isFirstFrame));
                }
                else
                    GetImagePath(true);
            }


        }
        /// <summary>
        /// 启动识别
        /// </summary>
        public void StartAnalyze()
        {
            if (!this.IsAnalyze)
            {
                this.IsAnalyze = true;

                while (this.IsAnalyze)
                {
                    Thread.Sleep(1000);
                }
            }
        }
    }
}
